// SPDX-License-Identifier: GPL-2.0
#define KMSG_COMPONENT "IPVS"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/sysctl.h>
#include <linux/list.h>
#include <linux/random.h>
#include "./ip_vs_fnat.h"

void ip_vs_fnat_new_conn_stats(struct ip_vs_stats *s)
{
	struct ip_vs_count_stats *cs = &s->count_stats;

	if ((s->threshold.flag & IP_VS_SVC_F_FLOW_CONTROL_CPS) &&
	    ((atomic64_read(&cs->conns) - cs->last_conns) >
	    s->threshold.threshold_con))
		s->ft_ovf_flag |= IP_VS_SVC_F_OVF_CPS;
	else
		s->ft_ovf_flag &= ~IP_VS_SVC_F_OVF_CPS;
}

void ip_vs_fnat_parallel_stats(struct ip_vs_stats *s)
{
	struct ip_vs_service *svc = container_of(s, struct ip_vs_service, stats);

	if ((s->threshold.flag & IP_VS_SVC_F_FLOW_CONTROL_PC) &&
	    (atomic64_read(&svc->svc_conns) > s->threshold.threshold_parallel_con))
		s->ft_ovf_flag |= IP_VS_SVC_F_OVF_PC;
	else
		s->ft_ovf_flag &= ~IP_VS_SVC_F_OVF_PC;
}

void ip_vs_fnat_calc_last_conn(struct ip_vs_stats *stat)
{
	struct ip_vs_count_stats *cs = &stat->count_stats;

	cs->last_conns = atomic64_read(&cs->conns);

}

void ip_vs_fnat_count_stats_init(struct ip_vs_count_stats *stats)
{
	atomic64_set(&stats->conns, 0);
	atomic64_set(&stats->inpkts, 0);
	atomic64_set(&stats->inbytes, 0);
	atomic64_set(&stats->outpkts, 0);
	atomic64_set(&stats->outbytes, 0);
	stats->last_conns = 0;
}

int ip_vs_fnat_count_init(struct net *net)
{
	struct netns_ipvs *ipvs = net_ipvs(net);

	ip_vs_fnat_count_stats_init(&ipvs->tot_stats.count_stats);

	return 0;
}

void ip_vs_fnat_copy_usr_stats(struct ip_vs_stats_user *dst, struct ip_vs_stats *src)
{
	struct ip_vs_count_stats *cstats = &src->count_stats;
	dst->conns = atomic64_read(&cstats->conns);
	dst->inpkts = atomic64_read(&cstats->inpkts);
	dst->outpkts = atomic64_read(&cstats->outpkts);
	dst->inbytes = atomic64_read(&cstats->inbytes);
	dst->outbytes = atomic64_read(&cstats->outbytes);

	/* we dont calculate rate */
	dst->cps = 0;
	dst->inpps = 0;
	dst->outpps = 0;
	dst->inbps = 0;
	dst->outbps = 0;
}

void ip_vs_fnat_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src)
{
	struct ip_vs_count_stats *cstats = &src->count_stats;
	dst->conns = atomic64_read(&cstats->conns);
	dst->inpkts = atomic64_read(&cstats->inpkts);
	dst->outpkts = atomic64_read(&cstats->outpkts);
	dst->inbytes = atomic64_read(&cstats->inbytes);
	dst->outbytes = atomic64_read(&cstats->outbytes);

	/* we dont calculate rate */
	dst->cps = 0;
	dst->inpps = 0;
	dst->outpps = 0;
	dst->inbps = 0;
	dst->outbps = 0;
}
void ip_vs_fnat_zero_stats(struct ip_vs_stats *stats)
{
	ip_vs_fnat_count_stats_init(&stats->count_stats);
}

void ip_vs_fnat_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
{
	struct ip_vs_dest *dest = cp->dest;
	struct netns_ipvs *ipvs = cp->ipvs;

	struct ip_vs_count_stats *dest_stats = NULL;
	struct ip_vs_count_stats *svc_stats = NULL;
	struct ip_vs_count_stats *ipvs_stats = NULL;

	if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
		dest_stats = &dest->stats.count_stats;
		svc_stats = &dest->svc->stats.count_stats;
		ipvs_stats = &ipvs->tot_stats.count_stats;

		atomic64_inc(&dest_stats->inpkts);
		atomic64_inc(&svc_stats->inpkts);
		atomic64_inc(&ipvs_stats->inpkts);

		atomic64_add((int64_t)skb->len, &dest_stats->inbytes);
		atomic64_add((int64_t)skb->len, &svc_stats->inbytes);
		atomic64_add((int64_t)skb->len, &ipvs_stats->inbytes);
	}
}

void ip_vs_fnat_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
{
	struct ip_vs_dest *dest = cp->dest;
	struct netns_ipvs *ipvs = cp->ipvs;

	struct ip_vs_count_stats *dest_stats = NULL;
	struct ip_vs_count_stats *svc_stats = NULL;
	struct ip_vs_count_stats *ipvs_stats = NULL;

	if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
		dest_stats = &dest->stats.count_stats;
		svc_stats = &dest->svc->stats.count_stats;
		ipvs_stats = &ipvs->tot_stats.count_stats;

		atomic64_inc(&dest_stats->outpkts);
		atomic64_inc(&svc_stats->outpkts);
		atomic64_inc(&ipvs_stats->outpkts);

		atomic64_add((int64_t)skb->len, &dest_stats->outbytes);
		atomic64_add((int64_t)skb->len, &svc_stats->outbytes);
		atomic64_add((int64_t)skb->len, &ipvs_stats->outbytes);
	}
}

void ip_vs_fnat_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
{
	struct netns_ipvs *ipvs = cp->ipvs;

	atomic64_inc(&cp->dest->stats.count_stats.conns);
	atomic64_inc(&svc->stats.count_stats.conns);
	atomic64_inc(&ipvs->tot_stats.count_stats.conns);

	ip_vs_fnat_new_conn_stats(&svc->stats);
	ip_vs_fnat_parallel_stats(&svc->stats);
}

